<!DOCTYPE html>
<html lang="zh-CN">
<head>
  <meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=2">
<meta name="theme-color" content="#222">
<meta name="generator" content="Hexo 6.3.0">
  <link rel="apple-touch-icon" sizes="180x180" href="/images/apple-touch-icon-next.png">
  <link rel="icon" type="image/png" sizes="32x32" href="/images/favicon-32x32-next.png">
  <link rel="icon" type="image/png" sizes="16x16" href="/images/favicon-16x16-next.png">
  <link rel="mask-icon" href="/images/logo.svg" color="#222">

<link rel="stylesheet" href="/css/main.css">


<link rel="stylesheet" href="/lib/font-awesome/css/all.min.css">

<script id="hexo-configurations">
    var NexT = window.NexT || {};
    var CONFIG = {"hostname":"chuangges.gitee.io","root":"/","scheme":"Pisces","version":"7.8.0","exturl":false,"sidebar":{"position":"right","display":"post","padding":18,"offset":12,"onmobile":false},"copycode":{"enable":true,"show_result":true,"style":"mac"},"back2top":{"enable":true,"sidebar":false,"scrollpercent":false},"bookmark":{"enable":false,"color":"#222","save":"auto"},"fancybox":false,"mediumzoom":false,"lazyload":false,"pangu":false,"comments":{"style":"tabs","active":null,"storage":true,"lazyload":false,"nav":null},"algolia":{"hits":{"per_page":10},"labels":{"input_placeholder":"Search for Posts","hits_empty":"We didn't find any results for the search: ${query}","hits_stats":"${hits} results found in ${time} ms"}},"localsearch":{"enable":false,"trigger":"auto","top_n_per_article":1,"unescape":false,"preload":false},"motion":{"enable":true,"async":false,"transition":{"post_block":"fadeIn","post_header":"slideDownIn","post_body":"slideDownIn","coll_header":"slideLeftIn","sidebar":"slideUpIn"}},"path":"search.xml"};
  </script>

  <meta name="description" content="Express、Koa、Egg">
<meta property="og:type" content="article">
<meta property="og:title" content="Node.js 常用框架">
<meta property="og:url" content="https://chuangges.gitee.io/2019/06/20/7-2-Node%E6%A1%86%E6%9E%B6/index.html">
<meta property="og:site_name" content="闯哥要去流浪了">
<meta property="og:description" content="Express、Koa、Egg">
<meta property="og:locale" content="zh_CN">
<meta property="article:published_time" content="2019-06-19T16:23:49.000Z">
<meta property="article:modified_time" content="2023-11-29T08:13:13.442Z">
<meta property="article:author" content="chuangges">
<meta property="article:tag" content="JS 扩展">
<meta name="twitter:card" content="summary">

<link rel="canonical" href="https://chuangges.gitee.io/2019/06/20/7-2-Node%E6%A1%86%E6%9E%B6/">


<script id="page-configurations">
  // https://hexo.io/docs/variables.html
  CONFIG.page = {
    sidebar: "",
    isHome : false,
    isPost : true,
    lang   : 'zh-CN'
  };
</script>

  <title>Node.js 常用框架 | 闯哥要去流浪了</title>
  






  <noscript>
  <style>
  .use-motion .brand,
  .use-motion .menu-item,
  .sidebar-inner,
  .use-motion .post-block,
  .use-motion .pagination,
  .use-motion .comments,
  .use-motion .post-header,
  .use-motion .post-body,
  .use-motion .collection-header { opacity: initial; }

  .use-motion .site-title,
  .use-motion .site-subtitle {
    opacity: initial;
    top: initial;
  }

  .use-motion .logo-line-before i { left: initial; }
  .use-motion .logo-line-after i { right: initial; }
  </style>
</noscript>

<link rel="alternate" href="/atom.xml" title="闯哥要去流浪了" type="application/atom+xml">
</head>

<body itemscope itemtype="http://schema.org/WebPage">
  <div class="container use-motion">
    <div class="headband"></div>

    <header class="header" itemscope itemtype="http://schema.org/WPHeader">
      <div class="header-inner"><div class="site-brand-container">
  <div class="site-nav-toggle">
    <div class="toggle" aria-label="切换导航栏">
      <span class="toggle-line toggle-line-first"></span>
      <span class="toggle-line toggle-line-middle"></span>
      <span class="toggle-line toggle-line-last"></span>
    </div>
  </div>

  <div class="site-meta">

    <a href="/" class="brand" rel="start">
      <span class="logo-line-before"><i></i></span>
      <h1 class="site-title">闯哥要去流浪了</h1>
      <span class="logo-line-after"><i></i></span>
    </a>
      <p class="site-subtitle" itemprop="description">愿你岁月可回首，且以深情共白头</p>
  </div>

  <div class="site-nav-right">
    <div class="toggle popup-trigger">
    </div>
  </div>
</div>




<nav class="site-nav">
  <ul id="menu" class="main-menu menu">
        <li class="menu-item menu-item-home">

    <a href="/" rel="section"><i class="fa fa-home fa-fw"></i>首页</a>

  </li>
        <li class="menu-item menu-item-tags">

    <a href="/tags/" rel="section"><i class="fa fa-tags fa-fw"></i>标签</a>

  </li>
        <li class="menu-item menu-item-categories">

    <a href="/categories/" rel="section"><i class="fa fa-th fa-fw"></i>分类</a>

  </li>
        <li class="menu-item menu-item-archives">

    <a href="/archives/" rel="section"><i class="fa fa-archive fa-fw"></i>归档</a>

  </li>
  </ul>
</nav>




</div>
    </header>

    
  <div class="back-to-top">
    <i class="fa fa-arrow-up"></i>
    <span>0%</span>
  </div>
  <div class="reading-progress-bar"></div>


    <main class="main">
      <div class="main-inner">
        <div class="content-wrap">
          

          <div class="content post posts-expand">
            

    
  
  
  <article itemscope itemtype="http://schema.org/Article" class="post-block" lang="zh-CN">
    <link itemprop="mainEntityOfPage" href="https://chuangges.gitee.io/2019/06/20/7-2-Node%E6%A1%86%E6%9E%B6/">

    <span hidden itemprop="author" itemscope itemtype="http://schema.org/Person">
      <meta itemprop="image" content="/images/chuang.png">
      <meta itemprop="name" content="chuangges">
      <meta itemprop="description" content="chuangge's blog.">
    </span>

    <span hidden itemprop="publisher" itemscope itemtype="http://schema.org/Organization">
      <meta itemprop="name" content="闯哥要去流浪了">
    </span>
      <header class="post-header">
        <h1 class="post-title" itemprop="name headline">
          Node.js 常用框架
        </h1>

        <div class="post-meta">
            <span class="post-meta-item">
              <span class="post-meta-item-icon">
                <i class="far fa-calendar"></i>
              </span>
              <span class="post-meta-item-text">发表于</span>

              <time title="创建时间：2019-06-20 00:23:49" itemprop="dateCreated datePublished" datetime="2019-06-20T00:23:49+08:00">2019-06-20</time>
            </span>
              <span class="post-meta-item">
                <span class="post-meta-item-icon">
                  <i class="far fa-calendar-check"></i>
                </span>
                <span class="post-meta-item-text">更新于</span>
                <time title="修改时间：2023-11-29 16:13:13" itemprop="dateModified" datetime="2023-11-29T16:13:13+08:00">2023-11-29</time>
              </span>
            <span class="post-meta-item">
              <span class="post-meta-item-icon">
                <i class="far fa-folder"></i>
              </span>
              <span class="post-meta-item-text">分类于</span>
                <span itemprop="about" itemscope itemtype="http://schema.org/Thing">
                  <a href="/categories/JS-%E6%89%A9%E5%B1%95/" itemprop="url" rel="index"><span itemprop="name">JS 扩展</span></a>
                </span>
            </span>

          <br>
            <span class="post-meta-item" title="本文字数">
              <span class="post-meta-item-icon">
                <i class="far fa-file-word"></i>
              </span>
                <span class="post-meta-item-text">本文字数：</span>
              <span>18k</span>
            </span>
            <span class="post-meta-item" title="阅读时长">
              <span class="post-meta-item-icon">
                <i class="far fa-clock"></i>
              </span>
                <span class="post-meta-item-text">阅读时长 &asymp;</span>
              <span>16 分钟</span>
            </span>
            <div class="post-description">Express、Koa、Egg</div>

        </div>
      </header>

    
    
    
    <div class="post-body" itemprop="articleBody">

      
        <h1 id="一、Express"><a href="#一、Express" class="headerlink" title="一、Express"></a>一、Express</h1><blockquote>
<p>Express 核心解决了 NodeJS 语法难用、没有统一格式的问题，然后 KOA 解决了 Express 中间件不支持异步的回调地狱并模仿 java 提出了 ctx 概念，最后 Egg 面向企业解决了多项目统一技术栈和代码风格的问题。后来为了支持 TS 有了装饰器、Hooks Midway、面向切面编程的 Nest。Express 只适合开发小型项目并作为新手入门级框架，其核心特性有：设置中间件来响应 HTTP 请求、定义路由来处理 HTTP 请求、向模板传递参数来动态渲染 HTML。</p>
</blockquote>
<h2 id="基础使用"><a href="#基础使用" class="headerlink" title="基础使用"></a>基础使用</h2>  <figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br><span class="line">68</span><br><span class="line">69</span><br><span class="line">70</span><br><span class="line">71</span><br><span class="line">72</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@title</span> NodeJS 原生语法实现服务器存在问题</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@param</span> 没有完善的路由机制：只能通过 createServer 回调判断地址。</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@param</span> 需要写很多基础代码：手动指定状态码和头部信息等而不好维护。</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@param</span> 原生 API 开发效率低：比如读取文件时还需要引入 fs 模块。</span></span><br><span class="line"><span class="comment">**/</span></span><br><span class="line"><span class="keyword">const</span> http = <span class="built_in">require</span>(<span class="string">&quot;http&quot;</span>);</span><br><span class="line"><span class="keyword">const</span> hostname = <span class="string">&quot;localhost&quot;</span>;   <span class="comment">// 主机名</span></span><br><span class="line"><span class="keyword">const</span> port = <span class="number">3000</span>;              <span class="comment">// 端口号</span></span><br><span class="line"><span class="keyword">const</span> server = http.<span class="title function_">createServer</span>(<span class="function">(<span class="params">req, res</span>) =&gt;</span> &#123;</span><br><span class="line">  <span class="keyword">if</span> (req.<span class="property">url</span> === <span class="string">&quot;/hello&quot;</span>) &#123;</span><br><span class="line">      res.<span class="property">statusCode</span> = <span class="number">200</span>;</span><br><span class="line">      res.<span class="title function_">setHeader</span>(<span class="string">&quot;Content-Type&quot;</span>, <span class="string">&quot;text/html&quot;</span>);</span><br><span class="line">      res.<span class="title function_">end</span>(<span class="string">&quot;Hello World\n&quot;</span>);</span><br><span class="line">  &#125; <span class="keyword">else</span> &#123;</span><br><span class="line">      res.<span class="property">statusCode</span> = <span class="number">404</span>;</span><br><span class="line">      res.<span class="title function_">end</span>();</span><br><span class="line">  &#125;</span><br><span class="line">&#125;);</span><br><span class="line">server.<span class="built_in">setTimeout</span>(<span class="number">5000</span>).<span class="title function_">listen</span>(port, <span class="function">() =&gt;</span> &#123;</span><br><span class="line">    <span class="variable language_">console</span>.<span class="title function_">log</span>(<span class="string">`Server running at http://<span class="subst">$&#123;hostname&#125;</span>:<span class="subst">$&#123;port&#125;</span>/`</span>);</span><br><span class="line">&#125;);</span><br><span class="line"></span><br><span class="line"></span><br><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@title</span> Express 提供了路由机制，内置了中间件、封装了请求和响应对象等。</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@param</span> 打印顺序 1 2 sleep：说明异步 await 并没有生效，Express 异步</span></span><br><span class="line"><span class="comment"> * 只能是通过回调方式但容易造成回调地狱，Koa 则通过洋葱圈模型和上下文 ctx </span></span><br><span class="line"><span class="comment"> * 可以在每个中间件去动态修改响应内容而不是像 Express 只在最后一个中间件。</span></span><br><span class="line"><span class="comment">**/</span> </span><br><span class="line"><span class="keyword">const</span> express = <span class="built_in">require</span>(<span class="string">&#x27;express&#x27;</span>);</span><br><span class="line"><span class="keyword">const</span> hostname = <span class="string">&#x27;localhost&#x27;</span>;</span><br><span class="line"><span class="keyword">const</span> port = <span class="number">3000</span>;</span><br><span class="line"><span class="keyword">const</span> app = <span class="title function_">express</span>();</span><br><span class="line"><span class="keyword">const</span> <span class="title function_">sleep</span> = (<span class="params">time</span>) =&gt; <span class="keyword">new</span> <span class="title class_">Promise</span>(<span class="function"><span class="params">resolve</span> =&gt;</span> <span class="built_in">setTimeout</span>(<span class="function">() =&gt;</span> &#123;</span><br><span class="line">    <span class="variable language_">console</span>.<span class="title function_">log</span>(<span class="string">&#x27;sleep timeout...&#x27;</span>)</span><br><span class="line">    <span class="title function_">resolve</span>()</span><br><span class="line">&#125;, time))</span><br><span class="line"><span class="comment">// 设置文件夹和模板</span></span><br><span class="line">app.<span class="title function_">set</span>(<span class="string">&quot;views&quot;</span>, **dirname + <span class="string">&quot;/views&quot;</span>);</span><br><span class="line">app.<span class="title function_">set</span>(<span class="string">&quot;view engine&quot;</span>, <span class="string">&quot;jade&quot;</span>)</span><br><span class="line"><span class="comment">// 静态资源</span></span><br><span class="line">app.<span class="title function_">use</span>(express.<span class="title function_">static</span>(**dirname + <span class="string">&#x27;/public&#x27;</span>))</span><br><span class="line"><span class="comment">// 动态渲染：路由中间件</span></span><br><span class="line">app.<span class="title function_">get</span>(<span class="string">&#x27;/hello&#x27;</span>, <span class="title function_">async</span>(req, res) =&gt; &#123;</span><br><span class="line">    <span class="keyword">await</span> <span class="title function_">sleep</span>(<span class="number">2000</span>)</span><br><span class="line">    res.<span class="title function_">send</span>(<span class="string">&#x27;Hello World&#x27;</span>);</span><br><span class="line">&#125;);</span><br><span class="line"><span class="comment">// 全局中间件：setTimeout 设置响应的超时时间和处理</span></span><br><span class="line">app.<span class="title function_">use</span>(<span class="function">(<span class="params">req, res, next</span>) =&gt;</span> &#123;</span><br><span class="line">    res.<span class="built_in">setTimeout</span>(<span class="number">30</span> * <span class="number">1000</span>, <span class="keyword">function</span> (<span class="params"></span>) &#123;</span><br><span class="line">        <span class="keyword">return</span> res.<span class="title function_">status</span>(<span class="number">408</span>).<span class="title function_">send</span>(<span class="string">&quot;请求超时&quot;</span>)</span><br><span class="line">    &#125;);</span><br><span class="line">    <span class="variable language_">console</span>.<span class="title function_">log</span>(<span class="number">1</span>)</span><br><span class="line">    <span class="title function_">next</span>()</span><br><span class="line">    <span class="variable language_">console</span>.<span class="title function_">log</span>(<span class="number">2</span>)</span><br><span class="line">&#125;)</span><br><span class="line"><span class="comment">// 放在最后的中间件代表匹配不到任何路由时返回 404 错误</span></span><br><span class="line">app.<span class="title function_">use</span>(<span class="string">&#x27;*&#x27;</span>, <span class="function">(<span class="params">req, res</span>) =&gt;</span> &#123;</span><br><span class="line">    res.<span class="title function_">status</span>(<span class="number">404</span>).<span class="title function_">end</span>()</span><br><span class="line">&#125;);</span><br><span class="line">app.<span class="title function_">listen</span>(port, <span class="function">() =&gt;</span> &#123;</span><br><span class="line">    <span class="variable language_">console</span>.<span class="title function_">log</span>(<span class="string">`Server running at http://<span class="subst">$&#123;hostname&#125;</span>:<span class="subst">$&#123;port&#125;</span>/`</span>);</span><br><span class="line">&#125;);</span><br><span class="line"></span><br><span class="line"></span><br><span class="line"><span class="comment">// 搭建应用：默认 localhost:3000</span></span><br><span class="line">npm install express express-generator -g </span><br><span class="line">express -e myApp    <span class="comment">// 使用 ejs 模板</span></span><br><span class="line">cd myApp</span><br><span class="line">npm install</span><br><span class="line">npm start</span><br></pre></td></tr></table></figure>


<h2 id="中间件"><a href="#中间件" class="headerlink" title="中间件"></a>中间件</h2><blockquote>
<p>本质是处理 HTTP 请求的函数。每个中间件都可以接收到请求对象 req、响应对象 res 和回调函数 next 三个参数，经过处理后可通过调用 next 传递给下一个中间件。主要类型如下：</p>
</blockquote>
<ul>
<li><strong>应用级中间件</strong>：使用 app.use&#x2F;method 绑定到 app 对象。</li>
<li><strong>路由级中间件</strong>：使用 router.use&#x2F;method 绑定到 express.Router() 对象。</li>
<li><strong>错误处理中间件</strong>：接收四个参数，用于处理应用中的错误。</li>
<li><strong>第三方中间件</strong>：安装后 require 到文件中使用，用来扩展功能。</li>
<li><strong>内置中间件</strong>：express.static 托管静态资源、express.json 解析请求。</li>
</ul>
  <figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@title</span> 定义方法</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@param</span> app.use：对客户端请求路径及其所有扩展结果进行匹配。</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@param</span> app.method：对客户端请求路径精确匹配，但会忽略锚点和 get 参数。</span></span><br><span class="line"><span class="comment"> * </span></span><br><span class="line"><span class="comment"> * <span class="doctag">@title</span> 响应方法</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@param</span> res.end：结束响应进程但不返回数据。</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@param</span> res.send：发送各种类型的响应。</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@param</span> res.json：发送 JSON 响应。</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@param</span> res.jsonp：在 JSONP 的支持下发送 JSON 响应。</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@param</span> res.render：呈现视图模板。</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@param</span> res.redirect：重定向请求。</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@param</span> res.sendFile：以八位元流形式发送文件。</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@param</span> res.download：提示将要下载文件。</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@param</span> res.sendStatus：设置响应状态码并发送其字符串表示。</span></span><br><span class="line"><span class="comment">**/</span></span><br><span class="line"><span class="comment">// 路径默认 &#x27;/&#x27; ，只匹配根路径</span></span><br><span class="line">app.<span class="title function_">get</span>(<span class="function">(<span class="params">req, res, next</span>) =&gt;</span> &#123; &#125;)</span><br><span class="line"><span class="comment">// 路径默认 &#x27;/&#x27;，匹配所有路径(都可以看作是 &#x27;/&#x27; 的扩展)</span></span><br><span class="line">app.<span class="title function_">use</span>(<span class="function">(<span class="params">req, res, next</span>) =&gt;</span> &#123; &#125;)</span><br><span class="line"></span><br><span class="line"><span class="comment">// 应用级</span></span><br><span class="line">app.<span class="title function_">use</span>(<span class="function">(<span class="params">req,res,next</span>) =&gt;</span> &#123;   </span><br><span class="line">    <span class="title function_">next</span>()   </span><br><span class="line">&#125;)</span><br><span class="line"></span><br><span class="line"><span class="comment">// 路由级</span></span><br><span class="line"><span class="keyword">var</span> router = express.<span class="title class_">Router</span>()</span><br><span class="line">router.<span class="title function_">get</span>(<span class="string">&#x27;/user/:id&#x27;</span>, <span class="function">(<span class="params">req, res, next</span>) =&gt;</span> &#123;</span><br><span class="line">    <span class="keyword">if</span> (req.<span class="property">params</span>.<span class="property">id</span> == <span class="number">0</span>) <span class="title function_">next</span>(<span class="string">&#x27;route&#x27;</span>)</span><br><span class="line">    <span class="keyword">else</span> <span class="title function_">next</span>()</span><br><span class="line">&#125;,<span class="function">(<span class="params">req, res, next</span>) =&gt;</span> &#123;</span><br><span class="line">    res.<span class="title function_">end</span>(<span class="string">&quot;Hello world&quot;</span>);</span><br><span class="line">&#125;)</span><br><span class="line"></span><br><span class="line"><span class="comment">// 错误处理</span></span><br><span class="line">app.<span class="title function_">use</span>(<span class="function">(<span class="params">err, req, res, next</span>) =&gt;</span> &#123;</span><br><span class="line">    res.<span class="title function_">status</span>(<span class="number">500</span>).<span class="title function_">send</span>(<span class="string">&#x27;Something broke&#x27;</span>)</span><br><span class="line">&#125;)</span><br><span class="line"></span><br><span class="line"><span class="comment">// 内置中间件 </span></span><br><span class="line">app.<span class="title function_">use</span>(express.<span class="title function_">static</span>(**dirname + <span class="string">&#x27;/public&#x27;</span>))</span><br></pre></td></tr></table></figure>


<h2 id="路由机制"><a href="#路由机制" class="headerlink" title="路由机制"></a>路由机制</h2><h3 id="路径规则"><a href="#路径规则" class="headerlink" title="路径规则"></a>路径规则</h3><ul>
<li>url 字符串不区分字母的大小写。</li>
<li>url 传递的数据通过冒号语法提取。</li>
<li>所有 get 方式提交的 url 参数及锚点均会被忽略。</li>
<li>所有路由中间件的书写顺序至关重要，只会执行首次匹配到的中间件。</li>
</ul>
  <figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// localhost:3000/tom/24：tom, 24</span></span><br><span class="line">app.<span class="title function_">get</span>(<span class="string">&#x27;/:name/:age&#x27;</span>, <span class="function">(<span class="params">req, res</span>) =&gt;</span> &#123;</span><br><span class="line">    <span class="variable language_">console</span>.<span class="title function_">log</span>(req.<span class="property">params</span>.<span class="property">name</span>, req.<span class="property">params</span>.<span class="property">age</span>)</span><br><span class="line">    res.<span class="title function_">send</span>()</span><br><span class="line">&#125;)</span><br><span class="line"></span><br><span class="line"><span class="comment">// localhost:3000/a#?name=tom </span></span><br><span class="line">app.<span class="title function_">get</span>(<span class="string">&#x27;/a&#x27;</span>, <span class="function">(<span class="params">req, res</span>) =&gt;</span> &#123;</span><br><span class="line">    res.<span class="title function_">send</span>(<span class="string">&#x27;success&#x27;</span>)</span><br><span class="line">&#125;)</span><br><span class="line"></span><br><span class="line"><span class="comment">// localhost:3000/admin/login：只执行第一个</span></span><br><span class="line">app.<span class="title function_">get</span>(<span class="string">&#x27;/:username/:method&#x27;</span>, <span class="function">(<span class="params">req, res</span>) =&gt;</span> &#123;</span><br><span class="line">    <span class="variable language_">console</span>.<span class="title function_">log</span>(<span class="number">1</span>)</span><br><span class="line">    res.<span class="title function_">end</span>()  <span class="comment">// 此处如果执行 next() 则会第二个</span></span><br><span class="line">&#125;)</span><br><span class="line">app.<span class="title function_">get</span>(<span class="string">&#x27;/admin/login&#x27;</span>, <span class="function">(<span class="params">req, res</span>) =&gt;</span> &#123;</span><br><span class="line">    <span class="variable language_">console</span>.<span class="title function_">log</span>(<span class="number">2</span>)</span><br><span class="line">    res.<span class="title function_">end</span>()</span><br><span class="line">&#125;)</span><br></pre></td></tr></table></figure>


<h3 id="路径匹配"><a href="#路径匹配" class="headerlink" title="路径匹配"></a>路径匹配</h3>  <figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// 1、字符串：/ 匹配根路径</span></span><br><span class="line">app.<span class="title function_">get</span>(<span class="string">&#x27;/&#x27;</span>, <span class="keyword">function</span> (<span class="params">req, res</span>) &#123; </span><br><span class="line">    res.<span class="title function_">send</span>(<span class="string">&#x27;root&#x27;</span>);</span><br><span class="line">&#125;)</span><br><span class="line">app.<span class="title function_">get</span>(<span class="string">&#x27;/about&#x27;</span>, <span class="keyword">function</span> (<span class="params">req, res</span>) &#123;</span><br><span class="line">    res.<span class="title function_">send</span>(<span class="string">&#x27;about&#x27;</span>);</span><br><span class="line">&#125;)</span><br><span class="line"><span class="comment">// /user、/user/5：去掉 ? 则只匹配 /user/5</span></span><br><span class="line">app.<span class="title function_">get</span>(<span class="string">&#x27;/user/:id?&#x27;</span>, <span class="keyword">function</span>(<span class="params">req, res</span>) &#123;</span><br><span class="line">    res.<span class="title function_">send</span>(<span class="string">&#x27;/users/:id?&#x27;</span>)</span><br><span class="line">&#125;)</span><br><span class="line"><span class="comment">// acd、abcd</span></span><br><span class="line">app.<span class="title function_">get</span>(<span class="string">&#x27;/ab?cd&#x27;</span>, <span class="keyword">function</span>(<span class="params">req, res</span>) &#123;</span><br><span class="line">    res.<span class="title function_">send</span>(<span class="string">&#x27;ab?cd&#x27;</span>)</span><br><span class="line">&#125;)</span><br><span class="line"><span class="comment">// /abe、/abcde</span></span><br><span class="line">app.<span class="title function_">get</span>(<span class="string">&#x27;/ab(cd)?e&#x27;</span>, <span class="keyword">function</span>(<span class="params">req, res</span>) &#123;</span><br><span class="line">    res.<span class="title function_">send</span>(<span class="string">&#x27;ab(cd)?e&#x27;</span>)</span><br><span class="line">&#125;)</span><br><span class="line"><span class="comment">// abcd、abbcd、abbbcd、...</span></span><br><span class="line">app.<span class="title function_">get</span>(<span class="string">&#x27;/ab+cd&#x27;</span>, <span class="keyword">function</span>(<span class="params">req, res</span>) &#123;</span><br><span class="line">    res.<span class="title function_">send</span>(<span class="string">&#x27;ab+cd&#x27;</span>)</span><br><span class="line">&#125;)</span><br><span class="line"><span class="comment">// abcd、abxcd、ab12cd、...</span></span><br><span class="line">app.<span class="title function_">get</span>(<span class="string">&#x27;/ab*cd&#x27;</span>, <span class="keyword">function</span>(<span class="params">req, res</span>) &#123;</span><br><span class="line">    res.<span class="title function_">send</span>(<span class="string">&#x27;ab*cd&#x27;</span>)</span><br><span class="line">&#125;)</span><br><span class="line"></span><br><span class="line"></span><br><span class="line"><span class="comment">// 2、正则表达式：包含 a、fly 结尾</span></span><br><span class="line">app.<span class="title function_">get</span>(<span class="regexp">/a/</span>, <span class="keyword">function</span>(<span class="params">req, res</span>) &#123;</span><br><span class="line">    res.<span class="title function_">send</span>(<span class="string">&#x27;/a/&#x27;</span>)</span><br><span class="line">&#125;)</span><br><span class="line">app.<span class="title function_">get</span>(<span class="regexp">/.*fly$/</span>, <span class="keyword">function</span>(<span class="params">req, res</span>) &#123;</span><br><span class="line">    res.<span class="title function_">send</span>(<span class="string">&#x27;/.*fly$/&#x27;</span>)</span><br><span class="line">&#125;)</span><br><span class="line"></span><br><span class="line"></span><br><span class="line"><span class="comment">// 3、数组：/abcd、/lmn、/pqr</span></span><br><span class="line">app.<span class="title function_">use</span>([<span class="string">&#x27;/abcd&#x27;</span>, <span class="regexp">/\/lmn|\/pqr/</span>], <span class="keyword">function</span> (<span class="params">req, res, next</span>) &#123;</span><br><span class="line">    <span class="title function_">next</span>()</span><br><span class="line">&#125;)</span><br></pre></td></tr></table></figure>


<h3 id="路由句柄"><a href="#路由句柄" class="headerlink" title="路由句柄"></a>路由句柄</h3>  <figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// 一个回调函数</span></span><br><span class="line">app.<span class="title function_">get</span>(<span class="string">&#x27;/example/a&#x27;</span>, <span class="keyword">function</span> (<span class="params">req, res</span>) &#123;</span><br><span class="line">    res.<span class="title function_">send</span>(<span class="string">&#x27;Hello from A&#x27;</span>)</span><br><span class="line">&#125;)</span><br><span class="line"><span class="comment">// 多个回调函数</span></span><br><span class="line">app.<span class="title function_">get</span>(<span class="string">&#x27;/example/b&#x27;</span>, <span class="keyword">function</span> (<span class="params">req, res, next</span>) &#123;</span><br><span class="line">    <span class="title function_">next</span>()</span><br><span class="line">&#125;, <span class="keyword">function</span> (<span class="params">req, res</span>) &#123;</span><br><span class="line">    res.<span class="title function_">send</span>(<span class="string">&#x27;Hello&#x27;</span>)</span><br><span class="line">&#125;);</span><br><span class="line"></span><br><span class="line"><span class="comment">// 回调函数数组</span></span><br><span class="line"><span class="keyword">var</span> a = <span class="keyword">function</span> (<span class="params">req, res, next</span>) &#123; <span class="title function_">next</span>() &#125;</span><br><span class="line"><span class="keyword">var</span> b = <span class="keyword">function</span> (<span class="params">req, res, next</span>) &#123; <span class="title function_">next</span>() &#125;</span><br><span class="line"><span class="keyword">var</span> c = <span class="keyword">function</span> (<span class="params">req, res</span>) &#123; res.<span class="title function_">send</span>(<span class="string">&#x27;Hello from C&#x27;</span>) &#125;</span><br><span class="line">app.<span class="title function_">get</span>(<span class="string">&#x27;/example/c&#x27;</span>, [a, b, c])</span><br><span class="line"></span><br><span class="line"><span class="comment">// 混合使用</span></span><br><span class="line">app.<span class="title function_">get</span>(<span class="string">&#x27;/example/d&#x27;</span>, [a, b], <span class="keyword">function</span> (<span class="params">req, res, next</span>) &#123;</span><br><span class="line">    <span class="title function_">next</span>()</span><br><span class="line">&#125;, <span class="keyword">function</span> (<span class="params">req, res</span>) &#123;</span><br><span class="line">    res.<span class="title function_">send</span>(<span class="string">&#x27;Hello from D&#x27;</span>);</span><br><span class="line">&#125;)</span><br></pre></td></tr></table></figure>


<h3 id="参数解析"><a href="#参数解析" class="headerlink" title="参数解析"></a>参数解析</h3>  <figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@param</span> req.param：已被弃用。</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@param</span> req.query：解析 GET 请求的查询字符串。</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@param</span> req.body：解析 POST 请求的数据，需引用 body-parser。</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@param</span> req.params：解析 GET/POST 请求的占位符数据，比如 /:name。</span></span><br><span class="line"><span class="comment">**/</span></span><br><span class="line"><span class="comment">// user/10</span></span><br><span class="line">app.<span class="title function_">get</span>(<span class="string">&quot;user/:id&quot;</span>, <span class="keyword">function</span> (<span class="params">req, res</span>) &#123;</span><br><span class="line">    res.<span class="title function_">send</span>(req.<span class="property">params</span>[<span class="string">&quot;id&quot;</span>])</span><br><span class="line">&#125;);</span><br><span class="line"></span><br><span class="line"><span class="comment">// user/?id=10</span></span><br><span class="line">app.<span class="title function_">get</span>(<span class="string">&quot;/user&quot;</span>, <span class="keyword">function</span> (<span class="params">req, res</span>) &#123;</span><br><span class="line">    res.<span class="title function_">send</span>(req.<span class="property">query</span>[<span class="string">&quot;id&quot;</span>])</span><br><span class="line">&#125;)</span><br><span class="line"></span><br><span class="line"><span class="comment">// 通过 body-parser 模块获取 post 请求数据</span></span><br><span class="line"><span class="keyword">let</span> bodyParser=<span class="built_in">require</span>(<span class="string">&#x27;body-parser&#x27;</span>)</span><br><span class="line">app.<span class="title function_">use</span>(bodyParser.<span class="title function_">urlencoded</span>(&#123; <span class="attr">extended</span>: <span class="literal">true</span> &#125;))</span><br><span class="line">app.<span class="title function_">post</span>(<span class="string">&quot;/login&quot;</span>, <span class="keyword">function</span> (<span class="params">req, res</span>) &#123;</span><br><span class="line">    res.<span class="title function_">send</span>(req.<span class="property">body</span>.<span class="property">name</span>, req.<span class="property">body</span>.<span class="property">password</span>)</span><br><span class="line">&#125;)</span><br></pre></td></tr></table></figure>


<h3 id="特殊路由"><a href="#特殊路由" class="headerlink" title="特殊路由"></a>特殊路由</h3>  <figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// 验证或处理路径参数</span></span><br><span class="line">router.<span class="title function_">param</span>(<span class="string">&#x27;id&#x27;</span>, <span class="keyword">function</span> (<span class="params">req, res, next, id</span>) &#123;</span><br><span class="line">    req.<span class="property">name</span> = name</span><br><span class="line">    <span class="title function_">next</span>()</span><br><span class="line">&#125;)</span><br><span class="line">router.<span class="title function_">get</span>(<span class="string">&#x27;/user/:id&#x27;</span>, <span class="keyword">function</span> (<span class="params">req, res</span>) &#123;</span><br><span class="line">    res.<span class="title function_">send</span>(<span class="string">&#x27;hello &#x27;</span> + req.<span class="property">name</span>)</span><br><span class="line">&#125;)</span><br><span class="line"></span><br><span class="line"></span><br><span class="line"><span class="comment">// 链式路由：一个路径处理多种请求</span></span><br><span class="line">app.<span class="title function_">route</span>(<span class="string">&#x27;/book&#x27;</span>)</span><br><span class="line">.<span class="title function_">get</span>(<span class="keyword">function</span>(<span class="params">req, res</span>) &#123;</span><br><span class="line">    res.<span class="title function_">send</span>(<span class="string">&#x27;Get a random book&#x27;</span>);</span><br><span class="line">&#125;)</span><br><span class="line">.<span class="title function_">post</span>(<span class="keyword">function</span>(<span class="params">req, res</span>) &#123;</span><br><span class="line">    res.<span class="title function_">send</span>(<span class="string">&#x27;Add a book&#x27;</span>);</span><br><span class="line">&#125;)</span><br></pre></td></tr></table></figure>


<h3 id="next-方法"><a href="#next-方法" class="headerlink" title="next 方法"></a>next 方法</h3>  <figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// 结果：1234</span></span><br><span class="line">app.<span class="title function_">get</span>(<span class="string">&#x27;/&#x27;</span>, <span class="keyword">function</span> (<span class="params">req, res</span>) &#123;</span><br><span class="line">    <span class="variable language_">console</span>.<span class="title function_">log</span>(<span class="number">1</span>)</span><br><span class="line">    <span class="title function_">next</span>()                <span class="comment">// 执行下一个处理函数    </span></span><br><span class="line">&#125;, <span class="keyword">function</span> (<span class="params">req, res</span>) &#123;</span><br><span class="line">    <span class="variable language_">console</span>.<span class="title function_">log</span>(<span class="number">2</span>)</span><br><span class="line">    <span class="title function_">next</span>(<span class="string">&#x27;route&#x27;</span>)         <span class="comment">// 执行下一个相同路由</span></span><br><span class="line">&#125;)</span><br><span class="line"></span><br><span class="line">app.<span class="title function_">get</span>(<span class="string">&#x27;/&#x27;</span>, <span class="keyword">function</span> (<span class="params">req, res</span>) &#123;</span><br><span class="line">    <span class="variable language_">console</span>.<span class="title function_">log</span>(<span class="number">3</span>)</span><br><span class="line">    <span class="title function_">next</span>()</span><br><span class="line">&#125;, <span class="keyword">function</span> (<span class="params">req, res</span>) &#123;</span><br><span class="line">    <span class="variable language_">console</span>.<span class="title function_">log</span>(<span class="number">4</span>)</span><br><span class="line">&#125;)</span><br></pre></td></tr></table></figure>


<h1 id="二、Koa"><a href="#二、Koa" class="headerlink" title="二、Koa"></a>二、Koa</h1><blockquote>
<p>Express 团队打造的进阶框架，基于 ES7 语法使用 Promise 实现异步而极大简化了异步代码。但是开发项目时缺少约束，多人开发时代码合并困难而不适合团队协作开发项目。主要特点有：<code>洋葱中间件模式</code> next 函数决定是否执行下一个中间件、<code>Context 对象</code>内部封装了 HTTP 请求对象和响应对象、<code>异步处理</code>是通过 Promise 并配合 async await。</p>
</blockquote>
<h2 id="基础使用-1"><a href="#基础使用-1" class="headerlink" title="基础使用"></a>基础使用</h2>  <figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// npm install koa koa-route</span></span><br><span class="line"><span class="keyword">const</span> <span class="title class_">Koa</span> = <span class="built_in">require</span>(<span class="string">&#x27;koa&#x27;</span>)</span><br><span class="line"><span class="keyword">const</span> app = <span class="keyword">new</span> <span class="title class_">Koa</span>()</span><br><span class="line"><span class="keyword">const</span> path = <span class="built_in">require</span>(<span class="string">&#x27;path&#x27;</span>)</span><br><span class="line"><span class="keyword">const</span> route = <span class="built_in">require</span>(<span class="string">&#x27;koa-route&#x27;</span>)</span><br><span class="line"></span><br><span class="line"><span class="comment">// 静态资源服务器：npm install koa-static</span></span><br><span class="line"><span class="keyword">const</span> serve = <span class="built_in">require</span>(<span class="string">&#x27;koa-static&#x27;</span>)</span><br><span class="line">app.<span class="title function_">use</span>(<span class="title function_">serve</span>(path.<span class="title function_">join</span>(**dirname, <span class="string">&#x27;/public&#x27;</span>)))</span><br><span class="line"></span><br><span class="line"></span><br><span class="line"><span class="comment">// 动态渲染页面：npm install koa-views ejs</span></span><br><span class="line"><span class="keyword">let</span> views = <span class="built_in">require</span>(<span class="string">&#x27;koa-views&#x27;</span>);</span><br><span class="line">app.<span class="title function_">use</span>(<span class="title function_">views</span>(**dirname,&#123;</span><br><span class="line">    <span class="attr">extension</span>:<span class="string">&#x27;ejs&#x27;</span> <span class="comment">//指定用ejs模板</span></span><br><span class="line">&#125;))</span><br><span class="line">app.<span class="title function_">use</span>(<span class="keyword">async</span> (ctx, next) =&gt; &#123;</span><br><span class="line">    <span class="comment">// 渲染 index.ejs</span></span><br><span class="line">    <span class="keyword">await</span> ctx.<span class="title function_">render</span>(<span class="string">&#x27;index&#x27;</span>, &#123;<span class="attr">name</span>:<span class="string">&#x27;cgp&#x27;</span>, <span class="attr">age</span>:<span class="number">9</span>&#125;)</span><br><span class="line">&#125;)</span><br><span class="line"></span><br><span class="line">app.<span class="title function_">listen</span>(<span class="number">3000</span>)</span><br><span class="line"></span><br><span class="line"></span><br><span class="line"><span class="comment">// 搭建应用：默认 localhost:3000</span></span><br><span class="line">npm install koa koa-generator -g </span><br><span class="line">koa2 project </span><br><span class="line">cd project</span><br><span class="line">npm install</span><br><span class="line">npm start</span><br></pre></td></tr></table></figure>


<h2 id="路由机制-1"><a href="#路由机制-1" class="headerlink" title="路由机制"></a>路由机制</h2>  <figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// 原生路由：通过 ctx.request.path 获取用户请求的路径</span></span><br><span class="line">app.<span class="title function_">use</span>(<span class="function"><span class="params">ctx</span> =&gt;</span> &#123;</span><br><span class="line">    <span class="keyword">if</span> (ctx.<span class="property">request</span>.<span class="property">path</span> == <span class="string">&#x27;/&#x27;</span>) &#123;</span><br><span class="line">        <span class="comment">// ctx.response.body的简写 </span></span><br><span class="line">        ctx.<span class="property">body</span> = <span class="string">&#x27;&lt;p&gt;Hello World&lt;/p&gt;&#x27;</span></span><br><span class="line">    &#125;</span><br><span class="line">&#125;)</span><br><span class="line"></span><br><span class="line"></span><br><span class="line"><span class="comment">// 路由中间件：npm install koa koa-router</span></span><br><span class="line"><span class="keyword">var</span> <span class="title class_">Koa</span> = <span class="built_in">require</span>(<span class="string">&#x27;koa&#x27;</span>);</span><br><span class="line"><span class="keyword">var</span> app = <span class="keyword">new</span> <span class="title class_">Koa</span>();</span><br><span class="line"><span class="keyword">var</span> router = <span class="built_in">require</span>(<span class="string">&#x27;koa-router&#x27;</span>)();</span><br><span class="line"></span><br><span class="line"><span class="comment">// 基础用法：常用 get、post、all</span></span><br><span class="line">router.<span class="title function_">get</span>(<span class="string">&#x27;/&#x27;</span>, <span class="keyword">async</span> (ctx, next) =&gt; &#123;</span><br><span class="line">    ctx.<span class="property">body</span> = <span class="string">&#x27;hello people&#x27;</span>;</span><br><span class="line">    <span class="keyword">await</span> <span class="title function_">next</span>()</span><br><span class="line">&#125;);</span><br><span class="line">router.<span class="title function_">get</span>(<span class="string">&#x27;/list&#x27;</span>, <span class="keyword">async</span> (ctx, next) =&gt; &#123;</span><br><span class="line">    ctx.<span class="property">body</span> = <span class="string">&#x27;list&#x27;</span>;</span><br><span class="line">&#125;);</span><br><span class="line"></span><br><span class="line"><span class="comment">// 传递参数</span></span><br><span class="line">router.<span class="title function_">get</span>(<span class="string">&#x27;/home&#x27;</span>, <span class="function">(<span class="params">ctx,next</span>) =&gt;</span> &#123; </span><br><span class="line">    <span class="variable language_">console</span>.<span class="title function_">log</span>(<span class="string">&#x27;/home?id=1 传递参数：&#x27;</span>, ctx.<span class="property">query</span>);</span><br><span class="line">    <span class="title function_">next</span>();</span><br><span class="line">&#125;);</span><br><span class="line">router.<span class="title function_">get</span>(<span class="string">&#x27;/user/:name&#x27;</span>, <span class="function">(<span class="params">ctx, next</span>) =&gt;</span> &#123;</span><br><span class="line">    <span class="variable language_">console</span>.<span class="title function_">log</span>(<span class="string">&#x27;/user/tom 传递参数：&#x27;</span>, ctx.<span class="property">params</span>.<span class="property">name</span>);</span><br><span class="line">    <span class="title function_">next</span>();</span><br><span class="line">&#125;);</span><br><span class="line">router.<span class="title function_">post</span>(<span class="string">&#x27;/user/register&#x27;</span>,<span class="title function_">async</span>(ctx, next) =&gt; &#123;</span><br><span class="line">    <span class="variable language_">console</span>.<span class="title function_">log</span>(ctx.<span class="property">request</span>.<span class="property">body</span>)</span><br><span class="line">&#125;)</span><br><span class="line"></span><br><span class="line"><span class="comment">// 重定向</span></span><br><span class="line">router.<span class="title function_">get</span>(<span class="string">&#x27;/login&#x27;</span>, <span class="keyword">async</span> (ctx, next) =&gt; &#123;</span><br><span class="line">    ctx.<span class="title function_">redirect</span>(<span class="string">&#x27;/user&#x27;</span>)</span><br><span class="line">&#125;)</span><br><span class="line"></span><br><span class="line"><span class="comment">// 嵌套路由</span></span><br><span class="line"><span class="keyword">let</span> home = <span class="keyword">new</span> <span class="title class_">Router</span>();</span><br><span class="line">home.<span class="title function_">get</span>(<span class="string">&#x27;/list&#x27;</span>, <span class="title function_">async</span>(ctx) =&gt; &#123;</span><br><span class="line">    ctx.<span class="property">body</span> = <span class="string">&quot;Home list&quot;</span>;</span><br><span class="line">&#125;).<span class="title function_">get</span>(<span class="string">&#x27;/todo&#x27;</span>, <span class="title function_">async</span>(ctx) =&gt; &#123;</span><br><span class="line">    ctx.<span class="property">body</span> =<span class="string">&#x27;Home ToDo&#x27;</span>;</span><br><span class="line">&#125;);</span><br><span class="line"><span class="keyword">let</span> user = <span class="keyword">new</span> <span class="title class_">Router</span>();</span><br><span class="line">user.<span class="title function_">get</span>(<span class="string">&#x27;/list&#x27;</span>, <span class="title function_">async</span>(ctx) =&gt; &#123;</span><br><span class="line">    ctx.<span class="property">body</span> = <span class="string">&quot;User list&quot;</span>;</span><br><span class="line">&#125;);</span><br><span class="line"></span><br><span class="line"><span class="comment">// 装载所有子路由</span></span><br><span class="line"><span class="keyword">let</span> router = <span class="keyword">new</span> <span class="title class_">Router</span>();</span><br><span class="line">router.<span class="title function_">use</span>(<span class="string">&#x27;/home&#x27;</span>, home.<span class="title function_">routes</span>(), home.<span class="title function_">allowedMethods</span>());</span><br><span class="line">router.<span class="title function_">use</span>(<span class="string">&#x27;/user&#x27;</span>, user.<span class="title function_">routes</span>(), user.<span class="title function_">allowedMethods</span>());</span><br><span class="line"></span><br><span class="line"><span class="comment">// 加载路由：当请求数据的方法与设置的方法不一致时会报错</span></span><br><span class="line">app.<span class="title function_">use</span>(router.<span class="title function_">routes</span>()).<span class="title function_">use</span>(router.<span class="title function_">allowedMethods</span>());</span><br><span class="line"></span><br><span class="line"><span class="comment">// 监听服务</span></span><br><span class="line">app.<span class="title function_">listen</span>(<span class="number">3000</span>);</span><br></pre></td></tr></table></figure>


<h2 id="中间件-1"><a href="#中间件-1" class="headerlink" title="中间件"></a>中间件</h2><blockquote>
<p>默认接收 <code>Context 对象、next 函数</code> 两个参数：&#96;Context 内部封装了原生 NodeJS 包含的 request、response 对象、next 被调用时会暂停当前中间件的运行并将控制传递给下一个。</p>
</blockquote>
  <figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@title</span> 第三方中间件：实例化</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@param</span> <span class="variable">kao</span>-views：模板渲染</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@param</span> <span class="variable">koa</span>-router：路由处理</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@param</span> <span class="variable">koa</span>-static：静态文件读取</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@param</span> <span class="variable">koa</span>-bodyparse：处理post请求的数据</span></span><br><span class="line"><span class="comment">**/</span></span><br><span class="line"><span class="keyword">var</span> router = <span class="built_in">require</span>(<span class="string">&#x27;koa-router&#x27;</span>)()</span><br><span class="line"></span><br><span class="line"><span class="comment">// 错误处理中间件</span></span><br><span class="line">app.<span class="title function_">use</span>(<span class="keyword">async</span> (ctx, next) =&gt; &#123;</span><br><span class="line">    <span class="keyword">try</span> &#123;</span><br><span class="line">        <span class="keyword">await</span> <span class="title function_">next</span>()</span><br><span class="line">    &#125; <span class="keyword">catch</span> (error) &#123;</span><br><span class="line">        ctx.<span class="property">response</span>.<span class="property">body</span> = &#123;</span><br><span class="line">            <span class="attr">code</span>: <span class="string">&#x27;404&#x27;</span>,</span><br><span class="line">            <span class="attr">message</span>: <span class="string">&#x27;服务器异常&#x27;</span>,</span><br><span class="line">            <span class="attr">desc</span>: error.<span class="property">message</span></span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;)</span><br><span class="line"></span><br><span class="line"><span class="comment">// 应用级中间件</span></span><br><span class="line">app.<span class="title function_">use</span>(<span class="keyword">async</span> (ctx, next) =&gt; &#123;</span><br><span class="line">    <span class="keyword">await</span> <span class="title function_">next</span>();</span><br><span class="line">    <span class="keyword">if</span>  (ctx.<span class="property">status</span> === <span class="number">404</span>)  &#123;</span><br><span class="line">        ctx.<span class="property">body</span> = <span class="string">&quot;404页面&quot;</span></span><br><span class="line">    &#125;</span><br><span class="line">&#125;)</span><br><span class="line"></span><br><span class="line"><span class="comment">// 路由级中间件</span></span><br><span class="line">router.<span class="title function_">get</span>(<span class="string">&#x27;/&#x27;</span>, <span class="keyword">async</span> ctx =&gt; &#123;</span><br><span class="line">    ctx.<span class="property">body</span> = <span class="string">&#x27;hello koa&#x27;</span>;</span><br><span class="line">&#125;)</span><br><span class="line"></span><br><span class="line"><span class="comment">// 启动路由</span></span><br><span class="line">app.<span class="title function_">use</span>(router.<span class="title function_">routes</span>()).<span class="title function_">use</span>(router.<span class="title function_">allowedMethods</span>())</span><br><span class="line"></span><br><span class="line"></span><br><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@title</span> 执行顺序</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@param</span> 多个中间件会形成堆栈结构，按先进后出顺序执行。</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@param</span> 路由会先经过应用级中间件，执行完 next 后再去匹配相应路由。</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@param</span> 匹配路由执行后还会再次进入应用级中间件执行 next 之后的逻辑。</span></span><br><span class="line"><span class="comment">**/</span></span><br><span class="line">app.<span class="title function_">use</span>(<span class="keyword">async</span> (ctx, next) =&gt; &#123;</span><br><span class="line">    <span class="variable language_">console</span>.<span class="title function_">log</span>(<span class="string">&#x27;1&#x27;</span>)</span><br><span class="line">    <span class="keyword">await</span> <span class="title function_">next</span>();</span><br><span class="line">    <span class="variable language_">console</span>.<span class="title function_">log</span>(<span class="string">&#x27;7&#x27;</span>)</span><br><span class="line">&#125;)</span><br><span class="line"></span><br><span class="line">app.<span class="title function_">use</span>(<span class="keyword">async</span> (ctx, next) =&gt; &#123;</span><br><span class="line">    <span class="variable language_">console</span>.<span class="title function_">log</span>(<span class="string">&#x27;2&#x27;</span>);</span><br><span class="line">    <span class="keyword">await</span> <span class="title function_">next</span>();</span><br><span class="line">    <span class="variable language_">console</span>.<span class="title function_">log</span>(<span class="string">&#x27;4&#x27;</span>)</span><br><span class="line">&#125;)</span><br><span class="line"></span><br><span class="line">router.<span class="title function_">get</span>(<span class="string">&#x27;/user&#x27;</span>, <span class="keyword">async</span> (ctx, next) =&gt; &#123;</span><br><span class="line">    <span class="variable language_">console</span>.<span class="title function_">log</span>(<span class="string">&#x27;3&#x27;</span>)</span><br><span class="line">    ctx.<span class="property">body</span> = <span class="string">&#x27;Hello Koa&#x27;</span>;</span><br><span class="line">&#125;)</span><br></pre></td></tr></table></figure>


<h2 id="常用功能"><a href="#常用功能" class="headerlink" title="常用功能"></a>常用功能</h2>  <figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br><span class="line">68</span><br><span class="line">69</span><br><span class="line">70</span><br><span class="line">71</span><br><span class="line">72</span><br><span class="line">73</span><br><span class="line">74</span><br><span class="line">75</span><br><span class="line">76</span><br><span class="line">77</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// 1、Cookies 设置中文时报错解决：框架自带属性，不需要引入中间件</span></span><br><span class="line">app.<span class="title function_">use</span>(<span class="keyword">async</span> (ctx, next) =&gt; &#123;</span><br><span class="line">    <span class="variable language_">this</span>.<span class="property">cookies</span>.<span class="title function_">set</span>(<span class="string">&#x27;test&#x27;</span>, <span class="keyword">new</span> <span class="title class_">Buffer</span>(<span class="string">&#x27;你好&#x27;</span>).<span class="title function_">toString</span>(<span class="string">&#x27;base64&#x27;</span>))</span><br><span class="line">    <span class="keyword">const</span> test = <span class="keyword">new</span> <span class="title class_">Buffer</span>(ctx.<span class="property">cookies</span>.<span class="title function_">get</span>(<span class="string">&#x27;test&#x27;</span>), <span class="string">&#x27;base64&#x27;</span>).<span class="title function_">toString</span>()</span><br><span class="line"></span><br><span class="line">    ctx.<span class="property">cookies</span>.<span class="title function_">set</span>(<span class="string">&#x27;name&#x27;</span>, <span class="built_in">encodeURIComponent</span>(<span class="string">&#x27;李&#x27;</span>))</span><br><span class="line">    <span class="keyword">const</span> name = <span class="built_in">decodeURIComponent</span>(ctx.<span class="property">cookies</span>.<span class="title function_">get</span>(<span class="string">&#x27;name&#x27;</span>))</span><br><span class="line">&#125;)</span><br><span class="line"></span><br><span class="line"></span><br><span class="line"><span class="comment">// 2、表单处理</span></span><br><span class="line"><span class="keyword">const</span> router = <span class="built_in">require</span>(<span class="string">&#x27;koa-router&#x27;</span>)();</span><br><span class="line"><span class="keyword">const</span> bodyParser = <span class="built_in">require</span>(<span class="string">&#x27;koa-bodyparse&#x27;</span>);</span><br><span class="line">app.<span class="title function_">use</span>(<span class="title function_">bodyParser</span>()); </span><br><span class="line">router.<span class="title function_">post</span>(<span class="string">&#x27;/add&#x27;</span>, <span class="keyword">async</span> (ctx) =&gt; &#123;</span><br><span class="line">    <span class="keyword">const</span> body = ctx.<span class="property">request</span>.<span class="property">body</span></span><br><span class="line">    ctx.<span class="property">body</span> = &#123; <span class="attr">name</span>: body.<span class="property">name</span> &#125;</span><br><span class="line">&#125;)</span><br><span class="line"></span><br><span class="line"></span><br><span class="line"><span class="comment">// 3、合并中间件：koa-compose 将多个中间件合成为一个</span></span><br><span class="line"><span class="keyword">const</span> compose = <span class="built_in">require</span>(<span class="string">&#x27;koa-compose&#x27;</span>)</span><br><span class="line"><span class="keyword">const</span> first = asycn (ctx, next) =&gt; &#123;</span><br><span class="line">    <span class="keyword">await</span> <span class="title function_">next</span>()</span><br><span class="line">&#125;</span><br><span class="line"><span class="keyword">const</span> <span class="title function_">second</span> = <span class="keyword">async</span> ctx =&gt; &#123;</span><br><span class="line">    ctx.<span class="property">body</span> = <span class="string">&#x27;Hello&#x27;</span></span><br><span class="line">&#125;</span><br><span class="line"><span class="keyword">const</span> middle = <span class="title function_">compose</span>([first, second])</span><br><span class="line">app.<span class="title function_">use</span>(middle)</span><br><span class="line"></span><br><span class="line"></span><br><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@title</span> 4、上传文件</span></span><br><span class="line"><span class="comment"> * &lt;form action=&quot;/upload&quot; method=&quot;POST&quot; enctype=&quot;multipart/form-data&quot;&gt;</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@param</span> 类型：图片 img_upload、单个文件 uploadfile、多个文件 uploadfiles</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@param</span> 实现：接收数据并分别创建可读流和可写流，然后让可读流通过管道写入可写流</span></span><br><span class="line"><span class="comment">*/</span></span><br><span class="line"><span class="keyword">const</span> koaBody = <span class="built_in">require</span>(<span class="string">&#x27;koa-body&#x27;</span>);</span><br><span class="line">app.<span class="title function_">use</span>(<span class="title function_">koaBody</span>(&#123;</span><br><span class="line">    <span class="attr">multipart</span>: <span class="literal">true</span>,</span><br><span class="line">    <span class="attr">formidable</span>: &#123; <span class="attr">maxFileSize</span>: <span class="number">20</span> * <span class="number">1024</span> * <span class="number">1024</span> &#125;</span><br><span class="line">&#125;));</span><br><span class="line"></span><br><span class="line">router.<span class="title function_">post</span>(<span class="string">&#x27;/img_upload&#x27;</span>, <span class="keyword">async</span> (ctx) =&gt; &#123;</span><br><span class="line">    <span class="keyword">const</span> data = ctx.<span class="property">request</span>.<span class="property">body</span>.<span class="property">files</span>.<span class="property">data</span>;</span><br><span class="line">    <span class="keyword">const</span> savePath = path.<span class="title function_">join</span>(<span class="string">`./files`</span>, data.<span class="property">name</span>)</span><br><span class="line">    <span class="keyword">const</span> reader = fs.<span class="title function_">createReadStream</span>(data.<span class="property">path</span>)</span><br><span class="line">    <span class="keyword">const</span> writer = fs.<span class="title function_">createWriteStream</span>(savePath)</span><br><span class="line">    <span class="keyword">const</span> pro = <span class="keyword">new</span> <span class="title class_">Promise</span>( <span class="function">(<span class="params">resolve, reject</span>) =&gt;</span> &#123;</span><br><span class="line">        <span class="keyword">var</span> stream = reader.<span class="title function_">pipe</span>(writer);</span><br><span class="line">        stream.<span class="title function_">on</span>(<span class="string">&#x27;finish&#x27;</span>, <span class="keyword">function</span> (<span class="params"></span>) &#123;</span><br><span class="line">            <span class="title function_">resolve</span>(<span class="string">`http://当前服务器地址<span class="subst">$&#123;data.name&#125;</span>`</span>);</span><br><span class="line">        &#125;)</span><br><span class="line">    &#125;)</span><br><span class="line">    ctx.<span class="property">response</span>.<span class="property">body</span> =  <span class="keyword">await</span> pro</span><br><span class="line">&#125;)</span><br><span class="line"></span><br><span class="line">router.<span class="title function_">post</span>(<span class="string">&#x27;/uploadfile&#x27;</span>, <span class="keyword">async</span> (ctx, next) =&gt; &#123;</span><br><span class="line">    <span class="keyword">const</span> file = ctx.<span class="property">request</span>.<span class="property">files</span>.<span class="property">file</span>; </span><br><span class="line">    <span class="keyword">const</span> reader = fs.<span class="title function_">createReadStream</span>(file.<span class="property">path</span>);</span><br><span class="line">    <span class="keyword">let</span> filePath = path.<span class="title function_">join</span>(**dirname, <span class="string">&#x27;upload/&#x27;</span>) + <span class="string">`/<span class="subst">$&#123;file.name&#125;</span>`</span>;</span><br><span class="line">    <span class="keyword">const</span> upStream = fs.<span class="title function_">createWriteStream</span>(filePath);</span><br><span class="line">    reader.<span class="title function_">pipe</span>(upStream);</span><br><span class="line">    <span class="keyword">return</span> ctx.<span class="property">body</span> = <span class="string">&quot;上传成功！&quot;</span>;</span><br><span class="line">&#125;)</span><br><span class="line"></span><br><span class="line">router.<span class="title function_">post</span>(<span class="string">&#x27;/uploadfiles&#x27;</span>, <span class="keyword">async</span> (ctx, next) =&gt; &#123;</span><br><span class="line">    <span class="keyword">const</span> files = ctx.<span class="property">request</span>.<span class="property">files</span>.<span class="property">file</span>; </span><br><span class="line">    <span class="keyword">for</span> (<span class="keyword">let</span> file <span class="keyword">of</span> files) &#123;</span><br><span class="line">        <span class="keyword">const</span> reader = fs.<span class="title function_">createReadStream</span>(file.<span class="property">path</span>);</span><br><span class="line">        <span class="keyword">let</span> filePath = path.<span class="title function_">join</span>(**dirname, <span class="string">&#x27;upload/&#x27;</span>) + <span class="string">`/<span class="subst">$&#123;file.name&#125;</span>`</span>;</span><br><span class="line">        <span class="keyword">const</span> upStream = fs.<span class="title function_">createWriteStream</span>(filePath);</span><br><span class="line">        reader.<span class="title function_">pipe</span>(upStream);</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="keyword">return</span> ctx.<span class="property">body</span> = <span class="string">&quot;上传成功！&quot;</span>;</span><br><span class="line">&#125;)</span><br></pre></td></tr></table></figure>


<h1 id="三、Egg"><a href="#三、Egg" class="headerlink" title="三、Egg"></a>三、Egg</h1><blockquote>
<p>koa 精简到几乎只有一个中间件的逻辑，实现其它复杂功能则需要自行扩展，所以它主要服务于个人用户或作为基础框架。Egg 则是由阿里 NodeJS 团队基于 koa2 封装的企业级框架，为了团队需要主要有以下更新：奉行<code>约定优于配置</code>(Express Koa 没有统一的格式和文件)、实现了<code>插件机制</code>(封装了通用业务并增加了生命周期)、提供了<code>定制化框架</code>的能力、多进程增强模型 (主进程 Master 通过 child_process cluster 模块分别创建 Agent Worker 子进程)。</p>
</blockquote>
<ul>
<li><strong>插件机制</strong>：启动前修改配置、请求校验等。<ul>
<li>插件没有独立的 Router、Controller，只用于某路由则可通过中间件。</li>
<li>插件没有 plugin.js，并且需要在 package.json eggPlugin 指定插件名称、强依赖、弱依赖等信息 (弱依赖可能不使用并不会被强制校验是否存在)。</li>
<li>生命周期：configWillLoad 配置文件即将加载、configDidLoad 配置加载完成但未生效、didLoad 配置加载完成、willReady 插件启动完毕、didReady 准备就绪、serverDidReady 应用启动完毕、beforeClose 应用即将关闭。</li>
</ul>
</li>
<li><strong>定制化框架的能力</strong>：基于 egg 来封装上层框架只需要扩展两个类。<ul>
<li>Application：App Worker 启动时会实例化 Application，单例。</li>
<li>Agent：Agent Worker 启动的时候会实例化 Agent，单例。</li>
<li>实现原理：基于类的继承，每层框架都继承上层并指定 eggPath，遍历原型链就能获取每层的框架路径，框架优先级为：部门框架 &gt; 企业框架 &gt; Egg。</li>
</ul>
</li>
<li><strong>多进程增强模型</strong><ul>
<li>Master 主进程：只有一个且第一个启动，主要负责子进程的管理和通信。</li>
<li>Agent 子进程：只有一个，主要处理公共资源的访问，比如文件监听。</li>
<li>Worker 子进程：用户设定的 CPU 个数，主要负责处理业务逻辑和用户请求。</li>
</ul>
</li>
</ul>
<h2 id="项目搭建"><a href="#项目搭建" class="headerlink" title="项目搭建"></a>项目搭建</h2>  <figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// npm init egg 寻找或下载可执行文件 create-egg (egg-init 别名)</span></span><br><span class="line">npm init egg --type=simple --registry=china</span><br><span class="line">npm run dev</span><br><span class="line"><span class="attr">http</span>:<span class="comment">//127.0.0.1:7001/</span></span><br><span class="line"></span><br><span class="line"><span class="comment">// 项目目录约定：Egg 约定优于配置的规则约定了统一的目录结构。</span></span><br><span class="line">egg-project</span><br><span class="line">├── package.<span class="property">json</span></span><br><span class="line">├── app.<span class="property">js</span>            <span class="comment">// 可选：worker 进程的初始化文件</span></span><br><span class="line">├── agent.<span class="property">js</span>          <span class="comment">// 可选：agent 进程的初始化文件</span></span><br><span class="line">├── app</span><br><span class="line">|   ├── router.<span class="property">js</span>     <span class="comment">// 路由配置</span></span><br><span class="line">│   ├── controller    <span class="comment">// 控制器：解析用户输入、加工处理、返回结果</span></span><br><span class="line">│   ├── model         <span class="comment">// 可选：数据库模型</span></span><br><span class="line">│   ├── service       <span class="comment">// 可选：业务逻辑层</span></span><br><span class="line">│   ├── middleware    <span class="comment">// 可选：中间件</span></span><br><span class="line">│   ├── schedule      <span class="comment">// 可选：定时任务</span></span><br><span class="line">│   ├── public        <span class="comment">// 可选：静态资源</span></span><br><span class="line">│   ├── view          <span class="comment">// 可选：模板文件</span></span><br><span class="line">│   └── extend        <span class="comment">// 可选：框架扩展</span></span><br><span class="line">│       ├── helper.<span class="property">js</span>     </span><br><span class="line">│       ├── request.<span class="property">js</span>    </span><br><span class="line">│       ├── response.<span class="property">js</span>   </span><br><span class="line">│       ├── context.<span class="property">js</span>    </span><br><span class="line">│       ├── application.<span class="property">js</span></span><br><span class="line">│       └── agent.<span class="property">js</span>        </span><br><span class="line">└── config</span><br><span class="line">    ├── plugin.<span class="property">js</span>        <span class="comment">// 需要加载的插件</span></span><br><span class="line">    └── config.&#123;env&#125;.<span class="property">js</span>  <span class="comment">// env 可以是 default、prod、test、local</span></span><br></pre></td></tr></table></figure>


<h2 id="路由配置"><a href="#路由配置" class="headerlink" title="路由配置"></a>路由配置</h2>  <figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// app/router.js</span></span><br><span class="line"><span class="variable language_">module</span>.<span class="property">exports</span> = <span class="function"><span class="params">app</span> =&gt;</span> &#123;</span><br><span class="line">    <span class="keyword">const</span> &#123; router, controller &#125; = app</span><br><span class="line">    <span class="comment">// 访问 news 时交由 controller/news.js 的 index 方法处理</span></span><br><span class="line">    router.<span class="title function_">get</span>(<span class="string">&#x27;/news&#x27;</span>, controller.<span class="property">news</span>.<span class="property">index</span>)</span><br><span class="line">    <span class="comment">// 通过冒号获 URL 命名参数并放入 ctx.params.x</span></span><br><span class="line">    router.<span class="title function_">get</span>(<span class="string">&#x27;/user/:id/:name&#x27;</span>, controller.<span class="property">user</span>.<span class="property">info</span>)</span><br><span class="line">    <span class="comment">// 通过自定义正则来捕获 URL 分组参数并放入 ctx.params</span></span><br><span class="line">    router.<span class="title function_">get</span>(<span class="regexp">/^\/package\/([\w-.]+\/[\w-.]+)$/</span>, </span><br><span class="line">    controller.<span class="property">package</span>.<span class="property">detail</span>)</span><br><span class="line">    <span class="comment">// 对 posts 按照 RESTful 风格映射到 controller/posts.js</span></span><br><span class="line">    router.<span class="title function_">resources</span>(<span class="string">&#x27;posts&#x27;</span>, <span class="string">&#x27;/posts&#x27;</span>, controller.<span class="property">posts</span>)</span><br><span class="line">    <span class="comment">// 内部重定向</span></span><br><span class="line">    app.<span class="property">router</span>.<span class="title function_">redirect</span>(<span class="string">&#x27;/&#x27;</span>, <span class="string">&#x27;/home/index&#x27;</span>, <span class="number">302</span>)</span><br><span class="line"></span><br><span class="line">    <span class="comment">// egg-router-plus 插件自动引入 app/router/**/*.js</span></span><br><span class="line">    <span class="keyword">const</span> subRouter = app.<span class="property">router</span>.<span class="title function_">namespace</span>(<span class="string">&#x27;/sub&#x27;</span>)</span><br><span class="line">    subRouter.<span class="title function_">get</span>(<span class="string">&#x27;/test&#x27;</span>, app.<span class="property">controller</span>.<span class="property">sub</span>.<span class="property">test</span>) </span><br><span class="line">    </span><br><span class="line">    <span class="comment">// 将路由映射拆分后的手动引入方式</span></span><br><span class="line">    <span class="built_in">require</span>(<span class="string">&#x27;./router/news&#x27;</span>)(app)</span><br><span class="line">    <span class="built_in">require</span>(<span class="string">&#x27;./router/admin&#x27;</span>)(app)</span><br><span class="line">&#125;;</span><br><span class="line"></span><br><span class="line"><span class="comment">// app/router/news.js </span></span><br><span class="line"><span class="variable language_">module</span>.<span class="property">exports</span> = <span class="function"><span class="params">app</span> =&gt;</span> &#123;</span><br><span class="line">    app.<span class="property">router</span>.<span class="title function_">get</span>(<span class="string">&#x27;/news/list&#x27;</span>, app.<span class="property">controller</span>.<span class="property">news</span>.<span class="property">list</span>)</span><br><span class="line">    app.<span class="property">router</span>.<span class="title function_">get</span>(<span class="string">&#x27;/news/detail&#x27;</span>, app.<span class="property">controller</span>.<span class="property">news</span>.<span class="property">detail</span>)</span><br><span class="line">&#125;;</span><br></pre></td></tr></table></figure>

<h2 id="中间件-2"><a href="#中间件-2" class="headerlink" title="中间件"></a>中间件</h2>  <figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@title</span> 初始化中间件</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@param</span> app/middleware/init.js：用于封装 ctx.body</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@param</span> ctx.returnBody(code, msg, data)：使用方式</span></span><br><span class="line"><span class="comment"> **/</span></span><br><span class="line"><span class="variable language_">module</span>.<span class="property">exports</span> = <span class="function"><span class="params">options</span> =&gt;</span> &#123;</span><br><span class="line">    <span class="keyword">return</span> <span class="keyword">async</span> (ctx, next) =&gt; &#123;</span><br><span class="line">        ctx.<span class="property">returnBody</span> = <span class="function">(<span class="params">code=<span class="number">200</span>, msg=<span class="string">&#x27;&#x27;</span>, data=&#123;&#125;</span>) =&gt;</span> &#123;</span><br><span class="line">            ctx.<span class="property">status</span> = code;</span><br><span class="line">            <span class="keyword">let</span> time = <span class="title class_">Math</span>.<span class="title function_">floor</span>(<span class="title class_">Date</span>.<span class="title function_">now</span>());</span><br><span class="line">            ctx.<span class="property">body</span> = &#123; code, msg, data, time &#125;;</span><br><span class="line">            <span class="keyword">return</span>;</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="keyword">await</span> <span class="title function_">next</span>();</span><br><span class="line">    &#125;</span><br><span class="line">&#125;;</span><br><span class="line">​</span><br><span class="line"><span class="comment">// token 校验中间件：登陆时设置相应 session</span></span><br><span class="line"><span class="variable language_">module</span>.<span class="property">exports</span> = <span class="function"><span class="params">options</span> =&gt;</span> &#123;</span><br><span class="line">    <span class="keyword">return</span> <span class="keyword">async</span> (ctx, next) =&gt; &#123;</span><br><span class="line">       <span class="keyword">let</span> tgt = ctx.<span class="property">request</span>.<span class="property">header</span>.<span class="property">tgt</span>;</span><br><span class="line">       <span class="keyword">if</span> (!tgt) <span class="keyword">return</span> ctx.<span class="title function_">returnBody</span>(<span class="number">401</span>, <span class="string">&#x27;token 不存在&#x27;</span>);</span><br><span class="line">       <span class="keyword">const</span> token = ctx.<span class="property">session</span>.<span class="property">tokenList</span> || [];</span><br><span class="line">       <span class="keyword">const</span> tgtFlag = tgt &amp;&amp; token.<span class="title function_">includes</span>(tgt);</span><br><span class="line">       <span class="keyword">if</span> (!tgtFlag) <span class="keyword">return</span> ctx.<span class="title function_">returnBody</span>(<span class="number">401</span>, <span class="string">&#x27;token 已失效&#x27;</span>);</span><br><span class="line">       <span class="keyword">await</span> <span class="title function_">next</span>();</span><br><span class="line">    &#125;</span><br><span class="line">&#125;;</span><br><span class="line"></span><br><span class="line"></span><br><span class="line"><span class="comment">// middleware/slow.js 慢查询中间件：请求时间超过指定阈值时打印日志</span></span><br><span class="line"><span class="variable language_">module</span>.<span class="property">exports</span> = <span class="function">(<span class="params">options, app</span>) =&gt;</span> &#123;</span><br><span class="line">    <span class="keyword">return</span> <span class="keyword">async</span> <span class="keyword">function</span> (<span class="params">ctx, next</span>) &#123;</span><br><span class="line">        <span class="keyword">const</span> startTime = <span class="title class_">Date</span>.<span class="title function_">now</span>()</span><br><span class="line">        <span class="keyword">await</span> <span class="title function_">next</span>()</span><br><span class="line">        <span class="keyword">const</span> consume = <span class="title class_">Date</span>.<span class="title function_">now</span>() - startTime</span><br><span class="line">        <span class="keyword">const</span> &#123; threshold = <span class="number">0</span> &#125; = options || &#123;&#125;</span><br><span class="line">        <span class="keyword">if</span> (consume &gt; threshold) &#123;</span><br><span class="line">            <span class="variable language_">console</span>.<span class="title function_">log</span>(<span class="string">`<span class="subst">$&#123;ctx.url&#125;</span>请求耗时<span class="subst">$&#123;consume&#125;</span>毫秒`</span>)</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br><span class="line"><span class="comment">// config.default.js 全局启用</span></span><br><span class="line"><span class="variable language_">module</span>.<span class="property">exports</span> = &#123;</span><br><span class="line">    <span class="attr">middleware</span>: [<span class="string">&#x27;slow&#x27;</span>],   <span class="comment">// 配置需要加载的中间件及其加载顺序</span></span><br><span class="line">    <span class="attr">slow</span>: &#123; <span class="attr">enable</span>: <span class="literal">true</span> &#125;  <span class="comment">// options 参数：指定路由时 match: &#x27;/api&#x27; </span></span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">// router.js 指定路由启用：controller 处理之前可添加任意中间件</span></span><br><span class="line"><span class="variable language_">module</span>.<span class="property">exports</span> = <span class="function"><span class="params">app</span> =&gt;</span> &#123;</span><br><span class="line">    <span class="keyword">const</span> &#123; router, middleware, controller &#125; = app</span><br><span class="line">    router.<span class="title function_">get</span>(<span class="string">&#x27;/api/home&#x27;</span>, middleware.<span class="title function_">slow</span>(&#123; <span class="attr">threshold</span>: <span class="number">1</span> &#125;),</span><br><span class="line">    controller.<span class="property">home</span>.<span class="property">index</span>)</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>





    </div>

    
    
    <div>
      
        <div>
    
        <div style="text-align:center;color: #ccc;font-size:14px;margin-top:20px;">-------------本文结束<i class="fa fa-paw"></i>感谢您的阅读-------------</div>
    
</div>

      
    </div>
    
        

<div>
<ul class="post-copyright">
  <li class="post-copyright-author">
    <strong>本文作者： </strong>chuangges
  </li>
  <li class="post-copyright-link">
    <strong>本文链接：</strong>
    <a href="https://chuangges.gitee.io/2019/06/20/7-2-Node%E6%A1%86%E6%9E%B6/" title="Node.js 常用框架">https://chuangges.gitee.io/2019/06/20/7-2-Node框架/</a>
  </li>
  <li class="post-copyright-license">
    <strong>版权声明： </strong>本博客所有文章除特别声明外，均采用 <a href="https://creativecommons.org/licenses/by-nc-sa/4.0/zh_CN" rel="external nofollow noopener noreferrer" target="_blank"><i class="fab fa-fw fa-creative-commons"></i>BY-NC-SA</a> 许可协议。转载请注明出处！
  </li>
</ul>
</div>


      <footer class="post-footer">

        


        
    <div class="post-nav">
      <div class="post-nav-item">
    <a href="/2019/06/16/7-1-Node%E5%9F%BA%E7%A1%80/" rel="prev" title="Node.js 主要内容">
      <i class="fa fa-chevron-left"></i> Node.js 主要内容
    </a></div>
      <div class="post-nav-item">
    <a href="/2019/06/24/8-1-%E7%A7%BB%E5%8A%A8%E7%AB%AF%E5%BC%80%E5%8F%91/" rel="next" title="移动端开发">
      移动端开发 <i class="fa fa-chevron-right"></i>
    </a></div>
    </div>
      </footer>
    
  </article>
  
  
  



          </div>
          

<script>
  window.addEventListener('tabs:register', () => {
    let { activeClass } = CONFIG.comments;
    if (CONFIG.comments.storage) {
      activeClass = localStorage.getItem('comments_active') || activeClass;
    }
    if (activeClass) {
      let activeTab = document.querySelector(`a[href="#comment-${activeClass}"]`);
      if (activeTab) {
        activeTab.click();
      }
    }
  });
  if (CONFIG.comments.storage) {
    window.addEventListener('tabs:click', event => {
      if (!event.target.matches('.tabs-comment .tab-content .tab-pane')) return;
      let commentClass = event.target.classList[1];
      localStorage.setItem('comments_active', commentClass);
    });
  }
</script>

        </div>
          
  
  <div class="toggle sidebar-toggle">
    <span class="toggle-line toggle-line-first"></span>
    <span class="toggle-line toggle-line-middle"></span>
    <span class="toggle-line toggle-line-last"></span>
  </div>

  <aside class="sidebar">
    <div class="sidebar-inner">

      <ul class="sidebar-nav motion-element">
        <li class="sidebar-nav-toc">
          文章目录
        </li>
        <li class="sidebar-nav-overview">
          站点概览
        </li>
      </ul>

      <!--noindex-->
      <div class="post-toc-wrap sidebar-panel">
          <div class="post-toc motion-element"><ol class="nav"><li class="nav-item nav-level-1"><a class="nav-link" href="#%E4%B8%80%E3%80%81Express"><span class="nav-number">1.</span> <span class="nav-text">一、Express</span></a><ol class="nav-child"><li class="nav-item nav-level-2"><a class="nav-link" href="#%E5%9F%BA%E7%A1%80%E4%BD%BF%E7%94%A8"><span class="nav-number">1.1.</span> <span class="nav-text">基础使用</span></a></li><li class="nav-item nav-level-2"><a class="nav-link" href="#%E4%B8%AD%E9%97%B4%E4%BB%B6"><span class="nav-number">1.2.</span> <span class="nav-text">中间件</span></a></li><li class="nav-item nav-level-2"><a class="nav-link" href="#%E8%B7%AF%E7%94%B1%E6%9C%BA%E5%88%B6"><span class="nav-number">1.3.</span> <span class="nav-text">路由机制</span></a><ol class="nav-child"><li class="nav-item nav-level-3"><a class="nav-link" href="#%E8%B7%AF%E5%BE%84%E8%A7%84%E5%88%99"><span class="nav-number">1.3.1.</span> <span class="nav-text">路径规则</span></a></li><li class="nav-item nav-level-3"><a class="nav-link" href="#%E8%B7%AF%E5%BE%84%E5%8C%B9%E9%85%8D"><span class="nav-number">1.3.2.</span> <span class="nav-text">路径匹配</span></a></li><li class="nav-item nav-level-3"><a class="nav-link" href="#%E8%B7%AF%E7%94%B1%E5%8F%A5%E6%9F%84"><span class="nav-number">1.3.3.</span> <span class="nav-text">路由句柄</span></a></li><li class="nav-item nav-level-3"><a class="nav-link" href="#%E5%8F%82%E6%95%B0%E8%A7%A3%E6%9E%90"><span class="nav-number">1.3.4.</span> <span class="nav-text">参数解析</span></a></li><li class="nav-item nav-level-3"><a class="nav-link" href="#%E7%89%B9%E6%AE%8A%E8%B7%AF%E7%94%B1"><span class="nav-number">1.3.5.</span> <span class="nav-text">特殊路由</span></a></li><li class="nav-item nav-level-3"><a class="nav-link" href="#next-%E6%96%B9%E6%B3%95"><span class="nav-number">1.3.6.</span> <span class="nav-text">next 方法</span></a></li></ol></li></ol></li><li class="nav-item nav-level-1"><a class="nav-link" href="#%E4%BA%8C%E3%80%81Koa"><span class="nav-number">2.</span> <span class="nav-text">二、Koa</span></a><ol class="nav-child"><li class="nav-item nav-level-2"><a class="nav-link" href="#%E5%9F%BA%E7%A1%80%E4%BD%BF%E7%94%A8-1"><span class="nav-number">2.1.</span> <span class="nav-text">基础使用</span></a></li><li class="nav-item nav-level-2"><a class="nav-link" href="#%E8%B7%AF%E7%94%B1%E6%9C%BA%E5%88%B6-1"><span class="nav-number">2.2.</span> <span class="nav-text">路由机制</span></a></li><li class="nav-item nav-level-2"><a class="nav-link" href="#%E4%B8%AD%E9%97%B4%E4%BB%B6-1"><span class="nav-number">2.3.</span> <span class="nav-text">中间件</span></a></li><li class="nav-item nav-level-2"><a class="nav-link" href="#%E5%B8%B8%E7%94%A8%E5%8A%9F%E8%83%BD"><span class="nav-number">2.4.</span> <span class="nav-text">常用功能</span></a></li></ol></li><li class="nav-item nav-level-1"><a class="nav-link" href="#%E4%B8%89%E3%80%81Egg"><span class="nav-number">3.</span> <span class="nav-text">三、Egg</span></a><ol class="nav-child"><li class="nav-item nav-level-2"><a class="nav-link" href="#%E9%A1%B9%E7%9B%AE%E6%90%AD%E5%BB%BA"><span class="nav-number">3.1.</span> <span class="nav-text">项目搭建</span></a></li><li class="nav-item nav-level-2"><a class="nav-link" href="#%E8%B7%AF%E7%94%B1%E9%85%8D%E7%BD%AE"><span class="nav-number">3.2.</span> <span class="nav-text">路由配置</span></a></li><li class="nav-item nav-level-2"><a class="nav-link" href="#%E4%B8%AD%E9%97%B4%E4%BB%B6-2"><span class="nav-number">3.3.</span> <span class="nav-text">中间件</span></a></li></ol></li></ol></div>
      </div>
      <!--/noindex-->

      <div class="site-overview-wrap sidebar-panel">
        <div class="site-author motion-element" itemprop="author" itemscope itemtype="http://schema.org/Person">
    <img class="site-author-image" itemprop="image" alt="chuangges" src="" data-original="/images/chuang.png">
  <p class="site-author-name" itemprop="name">chuangges</p>
  <div class="site-description" itemprop="description">chuangge's blog.</div>
</div>
<div class="site-state-wrap motion-element">
  <nav class="site-state">
      <div class="site-state-item site-state-posts">
          <a href="/archives/">
        
          <span class="site-state-item-count">39</span>
          <span class="site-state-item-name">日志</span>
        </a>
      </div>
      <div class="site-state-item site-state-categories">
            <a href="/categories/">
          
        <span class="site-state-item-count">7</span>
        <span class="site-state-item-name">分类</span></a>
      </div>
      <div class="site-state-item site-state-tags">
            <a href="/tags/">
          
        <span class="site-state-item-count">7</span>
        <span class="site-state-item-name">标签</span></a>
      </div>
  </nav>
</div>
  <div class="links-of-author motion-element">
      <span class="links-of-author-item">
        <a href="https://github.com/chuangges" title="GitHub → https:&#x2F;&#x2F;github.com&#x2F;chuangges" rel="external nofollow noopener noreferrer" target="_blank"><i class="fab fa-github fa-fw"></i>GitHub</a>
      </span>
      <span class="links-of-author-item">
        <a href="https://stackoverflow.com/yourname" title="StackOverflow → https:&#x2F;&#x2F;stackoverflow.com&#x2F;yourname" rel="external nofollow noopener noreferrer" target="_blank"><i class="fab fa-stack-overflow fa-fw"></i>StackOverflow</a>
      </span>
  </div>


  <div class="links-of-blogroll motion-element">
    <div class="links-of-blogroll-title"><i class="fa fa-link fa-fw"></i>
      友情链接
    </div>
    <ul class="links-of-blogroll-list">
        <li class="links-of-blogroll-item">
          <a href="https://juejin.im/" title="https:&#x2F;&#x2F;juejin.im&#x2F;" rel="external nofollow noopener noreferrer" target="_blank">掘金</a>
        </li>
        <li class="links-of-blogroll-item">
          <a href="https://www.nowcoder.com/" title="https:&#x2F;&#x2F;www.nowcoder.com&#x2F;" rel="external nofollow noopener noreferrer" target="_blank">牛客网</a>
        </li>
        <li class="links-of-blogroll-item">
          <a href="https://www.docschina.org/" title="https:&#x2F;&#x2F;www.docschina.org&#x2F;" rel="external nofollow noopener noreferrer" target="_blank">印记中文</a>
        </li>
        <li class="links-of-blogroll-item">
          <a href="http://tool.lu/" title="http:&#x2F;&#x2F;tool.lu&#x2F;" rel="external nofollow noopener noreferrer" target="_blank">在线工具</a>
        </li>
        <li class="links-of-blogroll-item">
          <a href="https://tinypng.com/" title="https:&#x2F;&#x2F;tinypng.com&#x2F;" rel="external nofollow noopener noreferrer" target="_blank">图片压缩</a>
        </li>
        <li class="links-of-blogroll-item">
          <a href="https://bigjpg.com/" title="https:&#x2F;&#x2F;bigjpg.com&#x2F;" rel="external nofollow noopener noreferrer" target="_blank">图片放大</a>
        </li>
        <li class="links-of-blogroll-item">
          <a href="https://tool.chinaz.com/" title="https:&#x2F;&#x2F;tool.chinaz.com&#x2F;" rel="external nofollow noopener noreferrer" target="_blank">站长工具</a>
        </li>
        <li class="links-of-blogroll-item">
          <a href="https://learn-anything.xyz/" title="https:&#x2F;&#x2F;learn-anything.xyz&#x2F;" rel="external nofollow noopener noreferrer" target="_blank">Learn-Anything</a>
        </li>
        <li class="links-of-blogroll-item">
          <a href="https://stackoverflow.com/" title="https:&#x2F;&#x2F;stackoverflow.com&#x2F;" rel="external nofollow noopener noreferrer" target="_blank">Stack-Overflow</a>
        </li>
    </ul>
  </div>

      </div>

    </div>
  </aside>
  <div id="sidebar-dimmer"></div>


      </div>
    </main>

    <footer class="footer">
      <div class="footer-inner">
        

        

<div class="copyright">
  
  &copy; 
  <span itemprop="copyrightYear">2024</span>
  <span class="with-love">
    <i class="fa fa-heart"></i>
  </span>
  <span class="author" itemprop="copyrightHolder">chuangge</span>
    <span class="post-meta-divider">|</span>
    <span class="post-meta-item-icon">
      <i class="fa fa-chart-area"></i>
    </span>
    <span title="站点总字数">1m</span>
    <span class="post-meta-divider">|</span>
    <span class="post-meta-item-icon">
      <i class="fa fa-coffee"></i>
    </span>
    <span title="站点阅读时长">15:14</span>
</div>
  <div class="powered-by">由 <a href="https://hexo.io/" class="theme-link" rel="external nofollow noopener noreferrer" target="_blank">Hexo</a> & <a href="https://pisces.theme-next.org/" class="theme-link" rel="external nofollow noopener noreferrer" target="_blank">NexT.Pisces</a> 强力驱动
  </div>

        








      </div>
    </footer>
  </div>

  
  <script src="/lib/anime.min.js"></script>
  <script src="/lib/velocity/velocity.min.js"></script>
  <script src="/lib/velocity/velocity.ui.min.js"></script>

<script src="/js/utils.js"></script>

<script src="/js/motion.js"></script>


<script src="/js/schemes/pisces.js"></script>


<script src="/js/next-boot.js"></script>




  















  

  

<script src="/live2dw/lib/L2Dwidget.min.js?094cbace49a39548bed64abff5988b05"></script><script>L2Dwidget.init({"pluginRootPath":"live2dw/","pluginJsPath":"lib/","pluginModelPath":"assets/","model":{"jsonPath":"/live2dw/assets/tororo.model.json"},"display":{"position":"left","width":180,"height":360},"mobile":{"show":false},"react":{"opacityDefault":1,"opacityOnHover":1},"log":false,"tagMode":false});</script>
        <style>
            [bg-lazy] {
                background-image: none !important;
                background-color: #eee !important;
            }
        </style>
        <script>
            window.imageLazyLoadSetting = {
                isSPA: false,
                preloadRatio: 1,
                processImages: null,
            };
        </script><script>window.addEventListener("load",function(){var t=/\.(gif|jpg|jpeg|tiff|png)$/i,r=/^data:image\/[a-z]+;base64,/;Array.prototype.slice.call(document.querySelectorAll("img[data-original]")).forEach(function(a){var e=a.parentNode;"A"===e.tagName&&(e.href.match(t)||e.href.match(r))&&(e.href=a.dataset.original)})});</script><script>!function(e){e.imageLazyLoadSetting.processImages=t;var n=e.imageLazyLoadSetting.isSPA,i=e.imageLazyLoadSetting.preloadRatio||1,r=o();function o(){var t=Array.prototype.slice.call(document.querySelectorAll("img[data-original]")),e=Array.prototype.slice.call(document.querySelectorAll("[bg-lazy]"));return t.concat(e)}function t(){n&&(r=o());for(var t,a=0;a<r.length;a++)0<=(t=(t=r[a]).getBoundingClientRect()).bottom&&0<=t.left&&t.top<=(e.innerHeight*i||document.documentElement.clientHeight*i)&&function(){var t,e,n,i,o=r[a];e=function(){r=r.filter(function(t){return o!==t})},(t=o).hasAttribute("bg-lazy")?(t.removeAttribute("bg-lazy"),e&&e()):(n=new Image,i=t.getAttribute("data-original"),n.onload=function(){t.src=i,t.removeAttribute("data-original"),e&&e()},t.src!==i&&(n.src=i))}()}function a(){clearTimeout(t.tId),t.tId=setTimeout(t,500)}t(),document.addEventListener("scroll",a),e.addEventListener("resize",a),e.addEventListener("orientationchange",a)}(this);</script></body>
</html>
